package com.gzsxy.esjy.service.netty.common.transactional;

import java.lang.reflect.Method;
import java.util.UUID;

import com.gzsxy.esjy.common.Assert;
import com.gzsxy.esjy.service.netty.common.lock.DistributedLock;
import com.gzsxy.esjy.service.netty.common.lock.Lock;
import com.gzsxy.esjy.service.netty.common.spel.AbstractSpelHandler;
import com.gzsxy.esjy.service.netty.common.transactional.annotations.GosunTransactional;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;


/**
 * 同步事务实现
 * @author zhangxiaolong
 * @date 2022/7/11 9:36
 */
@Aspect
@Component
public class GosunTransactionalHandler extends AbstractSpelHandler {

	/**
	 * 同步锁最大等待时间
	 */
	@Value("${gosun.lock.maxWaitTime:0}")
	private long maxWaitTime;

	/**
	 * 同步锁判断间隔时间
	 */
	@Value("${gosun.lock.timeInterval:0}")
	private long timeInterval;

	@Autowired
	private DistributedLock myDistributedLock;

	@Autowired(required = false)
	private DataSourceTransactionManager txManager;

	@Pointcut("@annotation(gosunTransactional)")
	public void gosunTransactionalPointcut(GosunTransactional gosunTransactional) {
	}

	/**
	 * 环绕切面
	 * 
	 * @param joinPoint
	 * @param gosunTransactional
	 * @return
	 * @throws Throwable
	 */
	@Around("gosunTransactionalPointcut(gosunTransactional)")
	public Object around(ProceedingJoinPoint joinPoint, GosunTransactional gosunTransactional) throws Throwable {
		String keyStr = gosunTransactional.key();
		// 不锁
		if (!gosunTransactional.lock()) {
			return executeTra(joinPoint, gosunTransactional);
		}
		return myDistributedLock.excute(e -> executeTra(joinPoint, gosunTransactional),new Lock(getKey(joinPoint, keyStr, gosunTransactional.gloablLock()), DistributedLock.DEFAULT_DEVICE,
				0 == maxWaitTime ? gosunTransactional.maxWaitTime() : maxWaitTime,
				0 == timeInterval ? gosunTransactional.timeInterval() : timeInterval));
	}

	/**
	 * 获取键
	 * 
	 * @param joinPoint
	 * @param keyStr
	 * @param b
	 * @return
	 * @throws Exception
	 */
	private String getKey(ProceedingJoinPoint joinPoint, String keyStr, boolean b) throws Exception {
		String className = getClassName(joinPoint);
		Method currentMethod = getCurrentMethod(joinPoint);
		String methodName = currentMethod.getName();

		// 获取方法参数类型的toString值(重载..)
		String argsStr = JSON.toJSONString(currentMethod.getParameterTypes());
		String prekey = className + "-" + methodName + "-" + argsStr + "-";
		if (b) {
			prekey = "global";
		}
		// 默认key（锁的是该方法下根据传参进行锁）
		if (StringUtils.hasText(keyStr)) {
			String customArgsKey = parseSpel(joinPoint, keyStr);
			return prekey + customArgsKey;
		} else {
			//锁整个方法体
			return prekey + argsStr;
		}
	}

	/**
	 * 获取类名
	 * 
	 * @param joinPoint
	 * @return
	 */
	private String getClassName(ProceedingJoinPoint joinPoint) {
		Object target = joinPoint.getTarget();
		return target.getClass().getName();
	}

	/**
	 * 获取Method对象
	 * 
	 * @param joinPoint
	 * @return
	 * @throws Exception
	 */
	private Method getCurrentMethod(ProceedingJoinPoint joinPoint) throws Exception {
		Signature signature = joinPoint.getSignature();
		MethodSignature ms = null;
		Assert.notTrue(signature instanceof MethodSignature, "该注解只能用于方法");
		ms = (MethodSignature) signature;
		Object target = joinPoint.getTarget();
		return target.getClass().getMethod(ms.getName(), ms.getParameterTypes());
	}

	/**
	 * 判断、执行事务
	 * 
	 * @param joinPoint
	 * @param gosunTransactional
	 * @param
	 * @return
	 * @throws Throwable
	 */
	private Object executeTra(ProceedingJoinPoint joinPoint, GosunTransactional gosunTransactional) {
		TransactionStatus status = null;
		// 事务的异常 -> catch -> rollBack
		try {
			if (gosunTransactional.transactional() && txManager != null) {
				// 获得事务状态
				status = txManager.getTransaction(beginTra(gosunTransactional.propagation()));
				//处理方法内容
				Object proceed = joinPoint.proceed();
//				commit(status);
				//提交事务
				txManager.commit(status);
				return proceed;
			} else {
				// 只读事务
				return joinPoint.proceed();
			}
		} catch (Throwable e) {
			if (null != status) {
				//事务回滚
				txManager.rollback(status);
			}
			Assert.isNotNull(null, "服务异常", 500, e);
		}
		return null ;
	}

	/**
	 * 开启事务
	 * 
	 * @return
	 */
	private DefaultTransactionDefinition beginTra(int type) {
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(type);
		def.setTimeout(30);
		def.setName(UUID.randomUUID().toString());
		return def;
	}

}
